// Scintilla source code edit control
/** @file LexAccessor.h
 ** Interfaces between Scintilla and lexers.
 **/
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#ifndef LEXACCESSOR_H
#define LEXACCESSOR_H

namespace Scintilla {

enum EncodingType { enc8bit, encUnicode, encDBCS };

class LexAccessor {
 private:
  IDocument* pAccess;
  enum { extremePosition = 0x7FFFFFFF };
  /** @a bufferSize is a trade off between time taken to copy the characters
	 * and retrieval overhead.
	 * @a slopSize positions the buffer before the desired position
	 * in case there is some backtracking. */
  enum { bufferSize = 4000, slopSize = bufferSize / 8 };
  char buf[bufferSize + 1];
  Sci_Position startPos;
  Sci_Position endPos;
  int codePage;
  enum EncodingType encodingType;
  Sci_Position lenDoc;
  char styleBuf[bufferSize];
  Sci_Position validLen;
  Sci_PositionU startSeg;
  Sci_Position startPosStyling;
  int documentVersion;

  void Fill(Sci_Position position) {
    startPos = position - slopSize;
    if (startPos + bufferSize > lenDoc) startPos = lenDoc - bufferSize;
    if (startPos < 0) startPos = 0;
    endPos = startPos + bufferSize;
    if (endPos > lenDoc) endPos = lenDoc;

    pAccess->GetCharRange(buf, startPos, endPos - startPos);
    buf[endPos - startPos] = '\0';
  }

 public:
  explicit LexAccessor(IDocument* pAccess_)
      : pAccess(pAccess_),
        startPos(extremePosition),
        endPos(0),
        codePage(pAccess->CodePage()),
        encodingType(enc8bit),
        lenDoc(pAccess->Length()),
        validLen(0),
        startSeg(0),
        startPosStyling(0),
        documentVersion(pAccess->Version()) {
    // Prevent warnings by static analyzers about uninitialized buf and styleBuf.
    buf[0] = 0;
    styleBuf[0] = 0;
    switch (codePage) {
      case 65001:
        encodingType = encUnicode;
        break;
      case 932:
      case 936:
      case 949:
      case 950:
      case 1361:
        encodingType = encDBCS;
    }
  }
  char operator[](Sci_Position position) {
    if (position < startPos || position >= endPos) {
      Fill(position);
    }
    return buf[position - startPos];
  }
  IDocument* MultiByteAccess() const {
    return pAccess;
  }
  /** Safe version of operator[], returning a defined value for invalid position. */
  char SafeGetCharAt(Sci_Position position, char chDefault = ' ') {
    if (position < startPos || position >= endPos) {
      Fill(position);
      if (position < startPos || position >= endPos) {
        // Position is outside range of document
        return chDefault;
      }
    }
    return buf[position - startPos];
  }
  bool IsLeadByte(char ch) const {
    return pAccess->IsDBCSLeadByte(ch);
  }
  EncodingType Encoding() const {
    return encodingType;
  }
  bool Match(Sci_Position pos, const char* s) {
    for (int i = 0; *s; i++) {
      if (*s != SafeGetCharAt(pos + i)) return false;
      s++;
    }
    return true;
  }
  char StyleAt(Sci_Position position) const {
    return pAccess->StyleAt(position);
  }
  Sci_Position GetLine(Sci_Position position) const {
    return pAccess->LineFromPosition(position);
  }
  Sci_Position LineStart(Sci_Position line) const {
    return pAccess->LineStart(line);
  }
  Sci_Position LineEnd(Sci_Position line) const {
    return pAccess->LineEnd(line);
  }
  int LevelAt(Sci_Position line) const {
    return pAccess->GetLevel(line);
  }
  Sci_Position Length() const {
    return lenDoc;
  }
  void Flush() {
    if (validLen > 0) {
      pAccess->SetStyles(validLen, styleBuf);
      startPosStyling += validLen;
      validLen = 0;
    }
  }
  int GetLineState(Sci_Position line) const {
    return pAccess->GetLineState(line);
  }
  int SetLineState(Sci_Position line, int state) {
    return pAccess->SetLineState(line, state);
  }
  // Style setting
  void StartAt(Sci_PositionU start) {
    pAccess->StartStyling(start);
    startPosStyling = start;
  }
  Sci_PositionU GetStartSegment() const {
    return startSeg;
  }
  void StartSegment(Sci_PositionU pos) {
    startSeg = pos;
  }
  void ColourTo(Sci_PositionU pos, int chAttr) {
    // Only perform styling if non empty range
    if (pos != startSeg - 1) {
      assert(pos >= startSeg);
      if (pos < startSeg) {
        return;
      }

      if (validLen + (pos - startSeg + 1) >= bufferSize) Flush();
      const char attr = static_cast<char>(chAttr);
      if (validLen + (pos - startSeg + 1) >= bufferSize) {
        // Too big for buffer so send directly
        pAccess->SetStyleFor(pos - startSeg + 1, attr);
      } else {
        for (Sci_PositionU i = startSeg; i <= pos; i++) {
          assert((startPosStyling + validLen) < Length());
          styleBuf[validLen++] = attr;
        }
      }
    }
    startSeg = pos + 1;
  }
  void SetLevel(Sci_Position line, int level) {
    pAccess->SetLevel(line, level);
  }
  void IndicatorFill(Sci_Position start, Sci_Position end, int indicator, int value) {
    pAccess->DecorationSetCurrentIndicator(indicator);
    pAccess->DecorationFillRange(start, value, end - start);
  }

  void ChangeLexerState(Sci_Position start, Sci_Position end) {
    pAccess->ChangeLexerState(start, end);
  }
};

struct LexicalClass {
  int value;
  const char* name;
  const char* tags;
  const char* description;
};

}  // namespace Scintilla

#endif
